Desarrollo notebook 2¶
Valores missing, outlier y correlaciones¶
En este notebook se realizara el estudio y preprocesamiento de las variables categóricas, continuas y booleanas, de acuerdo con la siguiente estrcutura:¶
Asignación del tipo de variable
- Conversión de tipo de datos
Separación en train y test estratificado
Visualización descriptiva de los datos
Gráficos de distribución de las variables
Tratamiento de variables continuas
- Gráfico de correlación
- Tratamiento de valores nulos
- Imputar valores nulos
Tratamiento de variables categóricas y booleanas
- Tratamiento de valores nulos
- Imputar valores nulos
Importar librerías¶
In [25]:
%matplotlib inline
import pandas as pd
import numpy as np
import seaborn as sns
from matplotlib import pyplot as plt
import plotly.express as px
import sklearn
from sklearn.impute import KNNImputer
import scipy.stats as ss
import warnings
from sklearn.model_selection import train_test_split
import sys
sys.path.append('/Users/miguelflores/Desktop/P1/practica1')
from funciones import funciones_auxiliares as f_aux
semilla = 42
pd.set_option("display.max_rows", 10000)
pd.set_option("display.max_columns", 10000)
pd.set_option("display.width", 10000)
Lectura de datos del preprocesado inicial¶
In [26]:
df = pd.read_csv('/Users/miguelflores/Desktop/CSV/pd_data_initial_preprocessing.csv').set_index('SK_ID_CURR')
df
Out[26]:
Asignación de tipo de variable (Categórica, Continua y Booleana)¶
A continuación, como previamente se había visualizado en el notebook 1, se realizará una categorización por cada tipo de variable, introduciendolas a listas, para posteriormente asignar el tipo de estas.¶
In [27]:
f_aux.clasificar_variables(df)
Out[27]:
In [28]:
f_aux.nueva_clasificar_variables(df)
Out[28]:
In [29]:
lista_var_bool, lista_var_cat, lista_var_con, lista_var_no_clasificadas = f_aux.nueva_clasificar_variables(df)
Conversión de tipo de datos¶
In [30]:
df[lista_var_cat] = df[lista_var_cat].astype("category")
df[lista_var_con] = df[lista_var_con].astype(float)
df[lista_var_con] = df[lista_var_con].apply(pd.to_numeric, errors='coerce')
df['TARGET'] = df['TARGET'].astype(int)
df.dtypes
Out[30]:
Separación en train y test estratificado¶
El propósito de este paso, es asegurar que las proporciones se mantengan equilibradas entre el conjunto de entrenamiento y el de prueba. Debido a que con esto se genera una mejor representatividad de los datos, permitiendo una evaluación más precisa del modelo.¶
In [31]:
X = df.drop('TARGET', axis=1) # Eliminar la columna 'TARGET' del conjunto de características
y = df['TARGET'] # Guardar la columna 'TARGET' como variable objetivo
In [32]:
X_pd_loan, X_pd_loan_test, y_pd_loan, y_pd_loan_test = train_test_split(X, y,
stratify=df['TARGET'],
test_size=0.2, random_state = semilla)
df_train = pd.concat([X_pd_loan, y_pd_loan],axis=1)
df_test = pd.concat([X_pd_loan_test, y_pd_loan_test],axis=1)
print('== Train\n', df_train['TARGET'].value_counts(normalize=True))
print('== Test\n', df_test['TARGET'].value_counts(normalize=True))
En esta sección, se utiliza una semilla definida al inicio del notebook para garantizar la reproducibilidad y consistencia en el proceso de división de los datos en conjuntos de entrenamiento y prueba. Esto asegura que los resultados obtenidos sean replicables en futuras ejecuciones del mismo código."¶
Visualización descriptiva de los datos¶
Por medio de las funciones nulos_columna( ) y nulos_filas( ), podemos analizar la consistencia de los datos, al identificar la cantidad de valores nulos por variable. Lo cual nos permite evaluar qué variables podrían aportar más al modelo y cuáles podrían tener un impacto limitado debido a su alto porcentaje de valores nulos.¶
In [33]:
f_aux.nulos_columna(df)
Out[33]:
In [34]:
f_aux.nulos_filas(df)
Out[34]:
Gráficos con distribibución de las variables¶
En la siguiente línea de código, se utiliza un bucle que itera sobre el tipo de variable. Dependiendo de si la variable es continua o categórica/booleana, se llama a la función plot_feature( ). Si la variable es continua, se generara un histograma y un boxplot en relación con la variable objetivo. Si la varaible es categórica o booleana, se mostrarán dos diagramas de barras: uno para la distribución general de la variable y otro en relación con la variable objetivo.¶
In [35]:
warnings.filterwarnings('ignore')
for i in list(df_train.columns):
if (df_train[i].dtype==float) & (i!='TARGET'):
print('Graficos de la variable: ' + i)
f_aux.plot_feature(df_train, col_name=i, isContinuous=True, target='TARGET')
elif i!='TARGET':
print('Graficos de la variable: ' + i)
f_aux.plot_feature(df_train, col_name=i, isContinuous=False, target='TARGET')
Conclusiones de los gráficos¶
En estas 121 gráficas, se pueden observar las variaciones tanto de manera individual como con respecto a la variable objetivo. Al plantear esta conclusión, es relevante comenzar desde lo particular hacia lo general. En un primer análisis, observamos aspectos individuales como el género, donde los hombres son quienes tienen una mayor tasa de pago del préstamo en comparación con las mujeres. En cuanto al nivel educativo, se evidencia que, a mayor nivel educativo, hay una mayor tendencia a saldar el préstamo (asociado con la variable 0). En términos de edad, las personas mayores tienen una mayor probabilidad de devolver el préstamo, lo que se refleja en las claras diferencias entre los rangos intercuartílicos del boxplot.¶
De manera más general, se destacan el tipo de trabajo y la organización en la que se labora. Se observa que las personas que trabajan en ambientes formales y bien establecidos, como grandes empresas, tienen mayores probabilidades de devolver el préstamo en tiempo y forma. En contraste, aquellos que desempeñan oficios o trabajos menos especializados, como los trabajadores de baja cualificación, personal de camareros y conductores, tienden a tener una menor tasa de pago puntual.¶
Finalmente, existen variables que resultan determinantes para el modelo, tales como el ingreso, la referencia de otros bancos, la situación de tu círculo cercano y el lugar de residencia. Estos factores son indicadores de la capacidad económica y la estabilidad de las personas, lo que afecta directamente su capacidad para afrontar pagos de préstamos.¶
Tratamiento de las variables continuas¶
A continuación, se tratan los valores missing, las correlaciones de las variables continuas y los outliers.¶
In [36]:
lista_var_con
Out[36]:
Por medio de la función de get_deviation_of_mean_perc( ), se determina que proporción de las variables continuas se situan fuera de un intervalo de confianza basado en la media y la desviación estándar, siendo multiplicada por el factor multiplier. En este caso la función nos da el número y porcentaje de valores fuera del rango, a la par de detertminar como se distribuyen estos valores extremos conforme a la variable objetivo.¶
In [54]:
f_aux.get_deviation_of_mean_perc(df_train, lista_var_con, target = 'TARGET', multiplier = 3)
Out[54]:
Conclusiones del impacto de las variables continuas con respecto a la variable objetivo¶
Cuando una variable presenta un mayor número de valores fuera del intervalo de confianza, nos indica una alta dispersión en los datos. Por lo que son más relevantes en la evaluación de riesgos por parte del banco, ya que van relacionadas a perfiles más diversos en los solicitantes, un ejemplo es la variable CNT_FAM_MEMBERS, que presenta 3,155 valores fuera del intervalo de confianza, indicando una mayor heterogeneidad en los tamaños de las familias, lo cual es relevante para la evaluar riesgos, asociandolo con el cumplimiento del préstamo.¶
Por otro lado, variables con un menor número de valores fuera del intervalo, un ejemplo es AMT_INCOME_TOTAL con solo 208 valores atípicos, sugiere que los solicitantes tienen ingresos similares. Indicando un perfil más homogéneo entre ellos en cuestión de esta variable. A partir de este análisis, es posible identificar variables clave para establecer perfiles generales de los solicitantes.¶
Gráfica de correlación¶
In [38]:
f_aux.get_corr_matrix(dataset = df_train[lista_var_con], metodo = 'pearson', size_figure = [10,8])
Out[38]:
In [39]:
corr = df_train[lista_var_con].corr('pearson')
new_corr = corr.abs()
new_corr.loc[:,:] = np.tril(new_corr, k=-1) # below main lower triangle of an array
new_corr = new_corr.stack().to_frame('correlation').reset_index().sort_values(by='correlation', ascending=False)
new_corr[new_corr['correlation']> 0.6]
Out[39]:
Conclusiones correlación entre variables¶
Existen valores extremadamente altos en la correlación entre ciertas variables, estas llegan a presentar el mismo tipo de variable, unicamente cambia el valor estadístico, un ejemplo es YEARS_BUILD_MEDI y YEARS_BUILD_AVG. La presencia de variables redundantes en un modelo predictivo afecta la estabilidad y la interpretrabilidad del modelo, por lo que es necesario identificarlas para sacarlas del modelo.¶
Asimismo, en las variables relacionadas con ingresos, crédito y situación laboral, generan casos con una relación proporcional directa, como lo es AMT_ANNUITY y AMT_CREDIT. Con el cual se puede establecer que si uno aumenta el otro lo hará a la par, es decir, si la cantidad de crédito requerido aumenta, la anualidad lo hará también. Esto de igual manera que la anterior sección, donde se establecen los valores dentro de un intervalo de confianza, nos ayuda a identificar patrones más complejos en los perfiles de los solicitantes.¶
Tratamiento valores nulos (Variables Continuas)¶
In [40]:
lista_var_con
Out[40]:
In [41]:
f_aux.get_percent_null_values_target(pd_loan = df_train, list_var_continuous = lista_var_con, target = 'TARGET')
Out[41]:
Conclusiones de porcentaje de valores nulos¶
Por medio del anterior análisis, es posible clasificar las variables en dos grupos: variables a imputar y variables a eliminar. Con esta categorización, se optimiza la calidad del conjunot de datos para los modelos predictivos. Sin embargo, es importante considerar también el significado detrás de los valores nulos. En este caso, los valores nulos pueden reflejar que el cliente no proporcionó ciertos documentos o información requerida. Por lo que dependiendo de la variable, puede llegar a ser un indicador de mayor riesgo.¶
Imputar valores nulos (Variables Continuas)¶
A continuación, se generaron dos listas para imputar los valores faltantes en el conjunto de datos, una para la imputación con la media y otra para la imputación con la mediana.¶
La decisión de utilizar estos métodos se basó en el porcentaje de valores faltantes en cada variable. Las que presentaban un porcentaje de valores nulos menor o igual al 30% se imputaron con la media, ya que se asumió que estos valores presentaban una distribución relativamente uniforme y no afectarían significativamente las relaciones entre las variables. Sustituir por la media es apropiado cuando los datos no contienen outliers representativos y presentan una distribución simétrica o normal.¶
Por otro lado, las variables con un porcentaje de valores nulos superior al 30% fueron imputadas con la mediana, debido a que esta es más robusta frente a los outliers y las distribuciones segmentadas. En el caso de que se hubieran imputado estos datos con la media, podría distorsionar el análisis debido a los valores atípicos o una distribución sesgada.¶
In [42]:
lista_imputar_media = []
lista_imputar_mediana = []
for variable in df_train[lista_var_con]:
if variable in ['AMT_ANNUITY', 'AMT_GOODS_PRICE', 'CNT_FAM_MEMBERS', 'EXT_SOURCE_2', 'OBS_30_CNT_SOCIAL_CIRCLE',
'DEF_30_CNT_SOCIAL_CIRCLE', 'OBS_60_CNT_SOCIAL_CIRCLE', 'DEF_60_CNT_SOCIAL_CIRCLE', 'DAYS_LAST_PHONE_CHANGE']:
lista_imputar_media.append(variable)
else:
lista_imputar_mediana.append(variable)
print("Lista Imputar Media:", lista_imputar_media)
print("Lista Imputar Mediana:", lista_imputar_mediana)
En la siguiente seccion, generamos una copia de la base de datos para así mantener la integridad de estos y facilitar la gestión a lo largo de las etapas del proceso de análisis.¶
In [43]:
copia_df_train = df_train.copy()
copia_df_test = df_test.copy()
In [44]:
# Imputar con Media
copia_df_train[lista_imputar_media] = copia_df_train[lista_imputar_media].apply(lambda x: x.fillna(x.mean()))
copia_df_test[lista_imputar_media] = copia_df_test[lista_imputar_media].apply(lambda x: x.fillna(x.mean()))
# Imputar con Mediana
copia_df_train[lista_imputar_mediana] = copia_df_train[lista_imputar_mediana].apply(lambda x: x.fillna(x.median()))
copia_df_test[lista_imputar_mediana] = copia_df_test[lista_imputar_mediana].apply(lambda x: x.fillna(x.median()))
Rectificamos que ya no se presentan valores nulos en este tipo de variable¶
In [45]:
# Filtrar los valores nulos solo para las variables de lista_var_con
nulos_train_con = copia_df_train[lista_var_con].isnull().sum()
nulos_test_con = copia_df_test[lista_var_con].isnull().sum()
# Imprimir los valores nulos por variable en los dos DataFrames
print("Valores nulos por variable (copia_df_train) :")
print(nulos_train_con)
print("\nValores nulos por variable (copia_df_test) :")
print(nulos_test_con)
Tratamiento valores nulos (Variables Categóricas y Variables Booleanas)¶
In [46]:
lista_var_cat
Out[46]:
In [47]:
lista_var_bool
Out[47]:
En cada lista de tipo de variable, generamos un bucle para que itere sobre ella y obtenemos las variables con valores nulos. Es importante mencionar que, de manera previa, se visualizó que la categoría de variables booleanas no presentaba valores nulos. Por esta razón, se genera el mensaje en el bucle.¶
In [48]:
col_cat = df_train.select_dtypes(include=['category']).columns.tolist()
for col in col_cat:
valores_nulos = df_train[col].isnull().sum()
tipo_variable = df_train[col].dtype
valores_unicos = df_train[col].unique()
if valores_nulos > 0:
print(f"Variable: {col}")
print(f" - Valores faltantes: {valores_nulos}")
print(f" - Tipo de variable: {tipo_variable}")
print(f" - Valores únicos: {valores_unicos}")
print("-" * 90)
In [49]:
col_bool = df_train.select_dtypes(include=[bool]).columns.tolist()
# Determinar que no existen valores nulos
hay_valores_nulos = False
for col in col_bool:
valores_nulos = df_train[col].isnull().sum()
tipo_variable = df_train[col].dtype
valores_unicos = df_train[col].unique()
if valores_nulos > 0:
hay_valores_nulos = True
print(f"Variable: {col}")
print(f" - Valores faltantes: {valores_nulos}")
print(f" - Tipo de variable: {tipo_variable}")
print(f" - Valores únicos: {valores_unicos}")
print("-" * 90)
else:
print(f"Variable: {col} - No tiene valores nulos")
# En el caso de que no se encuentren valores nulos
if not hay_valores_nulos:
print("Ninguna variable tiene valores nulos.")
Valor de Cramérs V.¶
El proósito de calcular este valor, es medir la fuerza de asociación entre dos variables categóricas, indicando que tan relacionados están. El rango de este valor va de 0 a 1, donde entre más cercano sea a uno, mayor es la fuerza de asociación. A pesar de que mide la relación entre variables, no nos determina la causalidad entre estas, debido a que no infiere en que una genere a la otra.¶
In [55]:
for variable in lista_var_cat:
print('-'*90)
print('Matriz de confusión {variiable} con respecto a TARGET:')
confusion_matriz = pd.crosstab(df_train['TARGET'], df_train[variable])
print(confusion_matriz)
valor_cramer = f_aux.cramers_v(confusion_matrix = confusion_matriz.values)
print('Valor de Cramers:', valor_cramer )
In [51]:
for variable in lista_var_bool:
print('-'*90)
print('Matriz de confusión {variiable} con respecto a TARGET:')
confusion_matriz = pd.crosstab(df_train['TARGET'], df_train[variable])
print(confusion_matriz)
valor_cramer = f_aux.cramers_v(confusion_matrix = confusion_matriz.values)
print('Valor de Cramers:', valor_cramer )
Conclusión sobre valor de Cramérs v.¶
A lo largo de los resultados obtenidos de los valores de Cramérs, obtenemos valores reducidos o débiles, donde donde los valores de Cramérs de las variables FLAG_MOBIL, FLAG_CONT_MOBILE, FLAG_EMAIL, FLAG_DOCUMENT_4, FLAG_DOCUMENT_7, FLAG_DOCUMENT_10, FLAG_DOCUMENT_12, FLAG_DOCUMENT_19, FLAG_DOCUMENT_20 y otras variables, tienen valores cercanos a 0, por lo que podemos empezar a establecer que estas variables las podemos considerar como irrelevantes para el modelado.¶
Es importante el no menospreciar variables con valores pequeños, más no cercanos a ceros, debido a que estos sumados nos podrían dar un impacto acumulativo al combinarlo con otras características. Algunas de las variables que presentan estos valores son FLAG_OWN_CAR, FLAG_PHONE, FLAG_DOCUMENT_3, REG_CITY_NOT_WORK_CITY, NAME_HOUSING_TYPE, y REG_CITY_NOT_LIVE_CITY¶
De igual manera tenemos una muestra de variables con mayor relevancia a las anteriores, como lo es CODE_GENDER (0.0545), indicando que el género esta relacionado con la variable objetivo, NAME_EDUCATION_TYPE (0.0566) representando el nivel educativo, OCCUPATION_TYPE (0.0811) tomando en cuenta la ocupación del solicitante y el ORGANIZATION_TYPE (0.0718), simbolizando el tipo de organización donde labora el solicitante. Estas variables de manera lógica aportan más que las anteriores, debido a que son aspectos que puedes escalar.¶
Imputar valores nulos (Variables Categóricas)¶
In [52]:
copia_df_train[lista_var_cat] = copia_df_train[lista_var_cat].astype("object").fillna("SIN VALOR").astype("category")
copia_df_test[lista_var_cat] = copia_df_test[lista_var_cat].astype("object").fillna("SIN VALOR").astype("category")
Guardar CSV¶
In [53]:
copia_df_train.to_csv('/Users/miguelflores/Desktop/CSV/train_df_preprocessing_missing_outlier.csv')
copia_df_test.to_csv('/Users/miguelflores/Desktop/CSV/test_df_preprocessing_missing_outlier.csv')